<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/6/22
 * Time: 13:23
 */

namespace app\admin\controller;


use think\Cache;
use think\Controller;
use think\Db;
use \FilesystemIterator;
use think\Exception;
class BackupController extends CommonController
{
    private $sizeOf             =   1; //    2M
    private $init_size          =   1024;
    private $tableName          =   '';
    private $init_backupDir     =   './backupData/';
    private $backupDir          =   '';
    private $subsection         =   true;
    private $ds                 =   "\n";
    private $DS                 =   '/';
    private $end                =   ';';
    private $pre_fix            =   '';
    private $sql                =   '';
    private $php_version        =   '';
    private $mysql_version      =   '';
    private $host               =   '';
    private $database           =   '';
    private $table_files        =   '';
    private $extension          =   '.sql';
    public $is_strictly         =   false;
    public $is_verify           =   true;
    public $initModel           =   false;
    private $connect            =   [
        // 数据库类型
        'type'            => 'mysql',
        // 服务器地址
        'hostname'        => '127.0.0.1',
        // 数据库名
        'database'        => 'jihui.cn',
        // 用户名
        'username'        => 'root',
        // 密码
        'password'        => '',
        // 端口
        'hostport'        => '',
        // 连接dsn
        'dsn'             => '',
        // 数据库连接参数
        'params'          => [],
        // 数据库编码默认采用utf8
        'charset'         => 'utf8',
        // 数据库表前缀
        'prefix'          => 'qhb_',
        // 数据集返回类型
        'resultset_type'  => 'array',
        // 是否需要进行SQL性能分析
        'sql_explain'     => false,
    ];

    /**
     * @return \think\response\Json 单表备份
     */
    public function     backup(){
        try{
            $result         =   $this->_initParam();
            if(false===$result){ return $this->_success($this->param,'正在异步备份数据请注意检查。。。。!');}
            $this->pre_fix  =   config('database.prefix');
            if(!empty($this->connect['prefix'])){
                //创建备份文件查
                $this->pre_fix=$this->connect['prefix'];
            }
            $this->host     =   config('database.hostname');
            $this->database =   config('database.database');
            if(!$this->_makeDir($this->backupDir)) throw new Exception('抱歉当前文件夹创建失败！',200);
            $result         =$this->_checkTable();
            if($result===false){throw new Exception('备份错误！',self::RESPONSE_SUCCESS);}
            return $this->_success($this->param,'已完整完成备份!');
        }catch (Exception $exception){
            return json(['data'=>'','info'=>$exception->getMessage(),'code'=>$exception->getCode()],$exception->getCode());
        }
    }
    /**
     * @param $tables |-多表备份采取异步备份以节约客户端等待时间
     */
    private function _multipleBackup($tables){
        $this->_asyncStore($tables);
        //异步备份数据 模拟异步
       return  async_send('http://www.rest.com/admin/async_backup/backupMultiple.html',['test'=>'test','yes'=>'no'],'POST');
    }


    private function _asyncStore($tables){
        if(extension_loaded('redis')){
            Cache::set('tables',serialize($tables));
        }else{
            $tables                         =   implode("\r\n",$tables);
            file_put_contents('./backupData/tables.text',$tables);
        }
    }
    /**
     * @throws Exception
     */
    private function _initParam(){

        list($this->subsection,$this->sizeOf,$this->backupDir,$this->tableName)=array_values($this->request->param());
        $table_name                     =   array_unique(explode(',',$this->tableName));
        if(is_array($table_name)){
            $this->_multipleBackup($table_name);
            return false;
        }
        if(empty($this->tableName)){throw new Exception('抱歉请填写备份的表名称',200);}
        $date                           =   date('Y-m-d',time());
        $this->subsection               =   $this->subsection==1?true:false;
        $this->init_size                =   empty($this->sizeOf)?$this->init_size:$this->sizeOf*$this->init_size;
        $this->backupDir                =   empty($this->backupDir)?$this->init_backupDir.$this->tableName.$this->DS.$date:$this->backupDir.$this->DS.$this->tableName.$this->DS.$date;
        return true;
    }

    /**
     * @return bool
     * @throws Exception
     * @throws \think\exception\PDOException
     */
    private function _checkTable(){
        $tablesInfo             =   db()->connect($this->connect)->query("SHOW TABLES LIKE '{$this->pre_fix}{$this->tableName}' ");
        if(sizeof($tablesInfo)==0){throw new Exception('抱歉你说备份的表不存在！',200);}
        $mysql_version          =   db()->connect($this->connect)->query('SELECT VERSION()  as version');
        list($mysql_version)    =   $mysql_version;
        $this->mysql_version    =   $mysql_version['version'];
        $this->php_version      =   PHP_VERSION;
        $this->_retrieve();
        //插入表结构
        return $this->_insert_table_structure();
    }

    /**
     * b备份头信息
     */
    private function _retrieve(){
        $this->sql.='-------------------------------------------------------------'.$this->ds;
        $this->sql.='----'.$this->ds;
        $this->sql.='----created DBManageClass power by lqc'.$this->ds;
        $this->sql.='----concat phone 18824267187'.$this->ds;
        $this->sql.='----copyRight '.date('Y').'  www.jihui365.cn'.$this->ds;
        $this->sql.='----Mysql version ('.$this->mysql_version.')'.$this->ds;
        $this->sql.='----PHP   version ('.$this->php_version.' )'.$this->ds;
        $this->sql.='----hostname ('.$this->host.')'.$this->ds;
        $this->sql.='----database ('.$this->database.')'.$this->ds;
        $this->sql.='----backup date( '.date('Y年 m月 d日 H 时 i分 s秒').')'.$this->ds;
        $this->sql.=$this->ds.$this->ds.$this->ds;
        $this->sql.='------------------------------------------------------------'.$this->ds.$this->ds.$this->ds;
    }

    /**
     * @return bool
     * @throws Exception
     * @throws \think\exception\PDOException
     */
    private function _insert_table_structure(){
        $this->sql.='------------------------------------------------------------'.$this->ds;
        $this->sql.='----backup tables name  '.$this->tableName.$this->ds;
        $this->sql.='---- table structure as follows'.$this->ds;
        $this->sql.='------------------------------------------------------------'.$this->ds.$this->ds;
        $this->sql.='DROP TABLE IF EXISTS `'.$this->tableName.'`'.$this->end.$this->ds;;
        $table_structure    =db()->connect($this->connect)->query('SHOW CREATE TABLE `'.$this->pre_fix.$this->tableName.'`');
        $this->sql.=$table_structure[0]['Create Table'].$this->end.$this->ds.$this->ds;
        $this->sql.='------------------------------------------------------------'.$this->ds.$this->ds.$this->ds;
        $this->sql.='----as follows insert statements '.$this->ds.$this->ds.$this->ds;
        $files     =        db()->connect($this->connect)->table($this->pre_fix.$this->tableName)->getTableInfo($this->pre_fix.$this->tableName);
        $this->table_files= $files['fields'];
        $data             = db()->connect($this->connect)->table($this->pre_fix.$this->tableName)->select();
        $i                = 0; $subsectionNumber=1; $length=(1024*1024*2);// 分卷文档编号
        do{
            $insert_sql   = $this->__insert_statements($data[$i]);
            $this->sql.=$insert_sql;
            if(strlen($this->sql)>=$length){
                $result = $this->_writeFile($subsectionNumber);
                if(false===$result){throw new Exception('抱歉数据备份失败！请检查！');}
                $this->sql        =''; //储存完毕之后置空sql在重新添加
                $subsectionNumber++;
            }
            unset($data[$i]);$i++;
        }while(count($data)!=0);
        //储存最后的不满足条件的sql语句
        $result         = $this->_writeFile($subsectionNumber);
        if(false===$result){throw new Exception('抱歉数据备份失败！请检查！');}
        return true;
    }

    private function __insert_statements($values){
        $insert_header                  =   "INSERT  INTO `".$this->pre_fix.$this->tableName ."` ( ".implode(',',$this->table_files) ." ) value( ";
        $values     = array_map(function(&$element){
           $element=empty(addslashes($element))?null:$element;
            if(!is_numeric($element)){
                $element="'".$element."'";
            }
            return $element;
        },$values);
        $values     =   implode(',',$values);
        return          $insert_sql=$insert_header.$values.' )'.$this->ds;
    }

    private function _makeDir($dir){
        // linux 可是使用posix系统标准来达到设置权限的目的。。前提是该执行程序应该具有比较高的权限
        umask(0);
        return  is_dir ($dir ) or $this->_makeDir(dirname( $dir )) and  mkdir ( $dir , 0755);
    }

    private function _writeFile($subsectionNumber=1){
        try{
            // 文件名称是 /backData/TableName/date/table_name + val + subsectionNumber.sal
            $filesName   =$this->backupDir.$this->DS.$this->tableName.chr(95).'val'.chr(95).$subsectionNumber.$this->extension;
            $fp          =fopen($filesName,'w+');
            if(!is_resource($fp)){ throw  new Exception('打开文件失败！');}
            flock($fp,LOCK_EX);
            fwrite($fp,$this->sql,strlen($this->sql));
            flock($fp,LOCK_UN);
            fclose($fp);
            return true;
        }catch (Exception $exception){return false;}
    }


    /**
     * 导入数据
     */
    public function reStore(){

    }

    /**
     *导入数据real操作
     */
    private  function _import(){

    }

    public function indexInfo(){
        try{
            //如果远程备份到本地也是可以的
            $baseDir            =               config('backupDir');
            $file               =               $this->_fileSys($baseDir);
            if(empty($file)){throw new Exception('抱歉还没初始化数据！');}
            $this->_beforeList($file);
            $data['name']       =               pathinfo($baseDir);
            $data['name']       =               $data['name']['filename'];
            $data['text']       =               $data['name'];
            $data['id']         =               0;
            $data['children']   =               $file;
            $return[]           =               $data;
            return  $this->_success($return,'已经为你找到已备份的数据');
        }catch (Exception $exception){
            $data= ['data'=>'','status'=>self::ERROR_CODE,'info'=>$exception->getMessage()];$code=$exception->getCode();
            return $this->_error($data,$code);
        }
    }


    /**
     * @param $list  |-拼接最终的数据
     * @return array |-返回拼接好的数据
     */
    protected function _beforeList(&$list)
    {
        $function               =function(&$paths){
           $new_array           =   [];
           foreach($paths as $key=>$value){
               $new_array[$key]['id']   =mt_rand(101,200);
               $new_array[$key]['name'] =$key;
               $new_array[$key]['text']  =$key;
               foreach ($value as $k=>$va) {
                   $new_array[$key]['children'][]=['id'=>mt_rand(201,300),'txt'=>$va,'name'=>$va];
               }
           }
           return array_values($new_array);
        };
        $new_node                   =   [];
        foreach($list as $key=>$value){
            $new_node[$key]['id']   =mt_rand(1,99);
            $new_node[$key]['name'] =   $key;
            $new_node[$key]['text'] =   $key;
            if(empty($value)){continue;}
            if(is_array($value)){$new_node[$key]['children']=$function($value);}
        }
       return  $list   =array_values($new_node);
    }

    /**
     * @param $baseDir
     * @return array
     */
    private function _fileSys($baseDir){
         //使用FileSystemIterator
        /*$this->system                   =    new FilesystemIterator($baseDir);
        static $file                    =    [];
        foreach($this->system as $key=>$sys){
            $file_name =pathinfo($sys->getPath());
            if( $sys->isDir() ){
                $file[$file_name['filename']]=$this->_fileSys($baseDir.$sys->getFilename().$this->DS);
            }else{
                $file[]        =  $sys->getFilename();
            }
        }
        return $file;*/
        $files=array();
        if(is_dir($baseDir))
        {
            if($handle=opendir($baseDir))
            {
                while(($file=readdir($handle))!==false)
                {
                    if($file!="." && $file!="..")
                    {
                        if(is_dir($baseDir.$this->DS.$file))
                        {
                            $files[$file]=$this->_fileSys($baseDir.$this->DS.$file);
                        } else {
                            $files[]=$baseDir.$this->DS.$file;
                        }
                    }
                }
                closedir($handle);
                return $files;
            }
        }
    }

    /**
     * @return bool|mixed
     */
    private function _unLock(){
        try{
          return   db()->connect($this->connect)->query('UNLOCK TABLES');
        }catch (Exception $exception){return false;}
    }
    /**
     * @param string $opt
     * @return bool|mixed
     */
    private function _lock_table($opt='WRITE'){
        try{
           return  db()->connect($this->connect)->query("LOCK TABLE  {$this->pre_fix}{$this->tableName} {$opt}");
        }catch (Exception $exception){return false;}
    }
}